DEVELOPPEMENT CLIENT-SERVEUR EN
JAVA
1
Présentation
JDBC (Java
DataBase Connectivity) est voisin d’ODBC. L’API JDBC est l’ensemble
des classes et interfaces permettant de travailler (connexion à la
base, exécution de requêtes et de procédures et fonctions stockées,
exploitation des résultats renvoyés…) sur des bases de données
relationnelles dans des programmes Java.
Les classes et
interfaces de l'API JDBC figurent dans le package
java.sql.
Il
est indispensable d’importer l’API JDBC dans tout programme se
servant de la technologie JDBC ; pour cela, il faut écrire
:
import
java.sql.* ;
Il
faut dans un premier temps établir une connexion à la base puis
effectuer les actions nécessaires sur les données.
Remarque concernant le package
java.sql :
2
Connexion à la base de données
2.1
Types de pilotes
Il
existe un ou plusieurs drivers JDBC spécifiques pour chaque SGBD
(système de gestion de bases de données : Access, Oracle, Informix,
...).
Les pilotes JDBC sont classés en 2
familles :
·
La famille de
pilotes non 100% Java :ce sont des pilotes qui utilisent
une partie écrite dans un autre langage que Java (souvent en C).
Ces pilotes doivent être installés sur la station cliente. Dans
cette 1ère famille, on distingue :
- Pilotes de type
1:
Pilotes accédant à une
base de données par l'intermédiaire d'une autre technologie (on
parle de passerelle ou pont). C’est typiquement le cas de la
passerelle JDBC-ODBC, le driver se nomme JdbcOdbcDriver et il est livré avec le JDK. Le pilote
convertit les appels de données Java en appels ODBC et les exécute
ensuite à l'aide du pilote ODBC.
- Pilotes de type
2: Pilotes
dits natifs. Il s'agit en fait d'un mélange de pilotes natifs et de
pilotes Java. Les appels JDBC sont convertis en appels natifs pour
le serveur de bases de données (Oracle, Sybase, ou autres)
généralement en C ou en C++.
·
La famille de
pilotes 100% Java :ils interrogent le gestionnaire de
base de données avec du code uniquement écrit en Java. Il n’y a
aucune installation à effectuer sur le client ce qui permet
d’interroger la base de données dans une applet. Dans cette
2ème famille, on distingue :
- Pilotes de type
3: Pilotes
convertissant les appels JDBC pour un middleware présent sur le
serveur et qui fait l’interface avec la BD.
- Pilotes de type
4: Pilotes
convertissant les appels JDBC directement en un protocole réseau
directement exploité par le SGBD.
2.1.1
Pilotes non 100% Java
2.1.1.1 Connexion via
jdbc-odbc (type 1)
a) Chargement du pilote
Il
faut d’abord charger le pilote pour permettre la connexion à la
base et le travail sur les données, cela se fait grâce à un appel à
la méthode forName (de la classe Class) à laquelle on transmet le
nom de la classe du pilote. Le pilote est alors enregistré auprès
du DriverManager (gestionnaire de pilotes) :
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}
catch(ClassNotFoundException e)
{
System.out.println("JDBC driver not found.");
}
Quel que soit le pilote qu’on
essaie de charger, c’est l’exception ClassNotFoundException qui est
levée (c’est à dire déclenchée) en cas d’échec. Il est obligatoire
d’intercepter cette exception ou bien de la renvoyer.
b) Connexion proprement dite
Nous allons partir de l'exemple
d'une base des voyages sous Access. Précisons bien que le SGBD
Access ne permet pas de travailler en vrai mode client-serveur mais
la façon de procéder est la même, la différence résidera bien sûr
dans les performances.
Cette base contient
les tables suivantes :
T_Voyages(NoVoyage,
LibVoyage, DateDebut, Duree, NbInscrits,NoDestination)
T_Destinations(NoDestination, LibDestination)
|
La
connexion à la base se fait via le DSN(data source name) BDVoyage
qui s’appuie sur le pilote Microsoft Access Driver.
Pour se connecter à une base, il
est nécessaire de déclarer une référence de type Connection et de
créer la connexion par un appel à la méthode getConnection
de la classe Drivermanager en indiquant la base de données à
l'aide d'une chaîne de connexion.
String cochaine =
"jdbc:odbc:Bdvoyage";
// chaîne de connexion avec DSN
Connection conn =
DriverManager.getConnection(cochaine); //
on effectue la connexion
//grâce à la méthode getConnection de la classe
DriverManager
Syntaxe générale pour la
connexion :
Connection
objetConnection =
DriverManager.getConnection(ChaîneDeConnexion,"NomUtilisateur","MotDePasse");
Remarques :
·
l’accès à une base
Oracle dans un programme Java peut être effectué via un DSN ODBC
(on choisira bien sûr un pilote pour Oracle lors de la création du
DSN). Mais dans le cas d’Oracle, il faudra impérativement préciser
le nom d’utilisateur et le mot de passe dans l’instruction de
connexion. Sur le serveur, il faudra que le listener Oracle ait été
lancé. Sur le client, il faudra avoir installé Net8 et le driver
odbc pour Oracle devra bien sûr être présent.
·
En cas d’échec de la
connexion, c’est l’exception SQLException qui est levée. Il est
obligatoire d’intercepter cette exception ou bien de la
renvoyer.
2.1.1.2 Connexion via une API
native à installer sur le client (type 2)
Nous illustrons ce paragraphe avec
Oracle.
Il
faudra installer Oracle OCI Driver sur le serveur et installer Net8
sur le client.
Il
faut que le classpath contienne le chemin de la bibliothèque des
drivers Oracle donc le classpath sera :
.;\\NomServeur\NomRépPartagé\Ora81\jdbc\lib\classes12.zip
NomRépPartagé est le nom de partage
donné au répertoire Oracle sur le serveur.
Le
listener devra être démarré sur le serveur.
a) Chargement du pilote
Il
faut d’abord charger le pilote pour permettre la connexion à la
base, ce chargement se fait grâce à un appel au DriverManager
(gestionnaire de pilotes) :
try
{
Class.forName("oracle.jdbc.driver.OracleDriver");
// ou DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
}
catch(ClassNotFoundException e)
{
System.out.println("JDBC driver not found.");
}
b) Connexion proprement dite
Nous allons partir de l'exemple de
la base des employés sous Oracle.
String cochaine =
"jdbc:oracle:oci8:@nomserv"; // nomserv désigne
le nom du
// service créé sous Net8
Connection conn =
DriverManager.getConnection(cochaine, "scott", "tiger");
// on effectue
// la connexion
2.1.2
Pilotes 100% Java
Nous illustrons ce paragraphe avec
Oracle.
Il
faudra que Oracle Thin Driver soit installé sur le serveur et il
faut que le classpath contienne le chemin de la bibliothèque des
drivers Oracle donc le classpath sera :
.;\\NomServeur\NomRépPartagé\Ora81\jdbc\lib\classes12.zip
NomRépPartagé est le nom de partage
donné au répertoire Oracle sur le serveur.
Le
listener devra être démarré sur le serveur.
2.1.2.1 Chargement du
pilote
Il
faut d’abord charger le pilote pour permettre la connexion à la
base, ce chargement se fait grâce à un appel au DriverManager
(gestionnaire de pilotes) :
try
{
Class.forName("oracle.jdbc.driver.OracleDriver");
// ou DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
}
catch(ClassNotFoundException e)
{
System.out.println("JDBC driver not found.");
}
2.1.2.2 Connexion proprement
dite
Nous allons partir de l'exemple de
la base des employés sous Oracle.
String cochaine =
"jdbc:oracle:thin:@192.168.1.36:1521:btsig";
Connection conn =
DriverManager.getConnection(cochaine, "scott", "tiger");
// on effectue
// la connexion
On
peut mettre le nom de la machine (sur Internet, il s'agira d'un nom
pleinement qualifié) à la place de l’adresse IP.
3
Le client-serveur de données
Quand la connexion est établie (si
aucune exception n'a été interceptée) on peut écrire des requêtes
pour construire, interroger ou mettre à jour la
base.
Plusieurs interfaces sont
utilisées :
Statement
:
C'est une interface
que l'application emploie pour transmettre des requêtes SQL à la
base.
ResultSet :
un ResultSet sera créé
lors de l’exécution d’une requête d’interrogation, c’est
l’ensemble des tuples du résultat de la requête, cet ensemble
pourra bien sûr être manipulé.
En
cas d’échec de l’une des méthodes permettant de travailler sur les
données, c’est l’exception SQLException qui est levée. Il est
obligatoire d’intercepter cette exception ou bien de la renvoyer.
C'est l'exception SQLException qui est levée en cas d'erreur pour
chacune des opérations vues dans la suite de ce cours.
3.1
Interrogation de la base
3.1.1
Requête et curseur (ResultSet)
Il
faut commencer par créer un Statement, il s’appuie sur la connexion
créée précédemment :
Statement requete =
conn.createStatement();
|
|
executeQuery exécute la requête et
place le résultat dans un ResultSet. Cet objet (nommé resultat dans
l’exemple ci-dessous) permet d'accéder aux données du résultat de
la requête :
ResultSet resultat =
requete.executeQuery ("select empno, ename, sal, hiredate
from emp");
|
Après la requête, le curseur est
positionné juste avant la première ligne du résultat mais le
1er enregistrement n’est pas accessible, il faut
effectuer un next() comme par la suite pour avancer
d’enregistrement en enregistrement.
La fin du ResultSet
est détectée par le
renvoi de la valeur false.
Il
faut donc effectuer une lecture initiale, puis prévoir une boucle
incluant l’utilisation des données et la lecture du
suivant.
boolean bool=resultat.next();
if (bool)
{
do
{
...
...
bool=resultat.next();
}
}
while (bool) ;
}
else
…
Facilité
d’écriture :
while (resultat.next()) et if
(resultat.next()) assurent la lecture de l’enregistrement et la
détection de la fin du ResultSet.
if (resultat.next())
{
do
{
...
...
}
while (resultat.next()) ;
}
else
…
Si
on ne souhaite pas distinguer le cas ResultSet vide, on utilisera
un TANTQUE et la structure du traitement sera la
suivante :
while (resultat.next())
{
...
...
}
3.1.2
Récupération des données
Pour récupérer les données des
différents champs, il faut utiliser la méthode getObject (qui
permet de récupérer les données sans se soucier de leurs types mais
attention, cela ne fonctionne généralement qu’avec le pont
JDBC/ODBC et à la condition qu’il ne s’agisse pas de données de
type réel ou date ou heure) ou utiliser les méthodes adaptées aux
types des données à récupérer mais alors il faut connaître le type
des données récupérées.
De
plus, si on utilise getObject, la donnée récupérée ne sera pas
« typée » et il sera par exemple impossible d’effectuer
des opérations arithmétiques sur cette donnée à moins de la
convertir.
Attention les colonnes sont numérotées à
partir de 1.
Principales méthodes de
récupération des champs suivant le type:
Type SQL
|
Type Java
|
Méthode de récupération
|
CHAR, VARCHAR,
VARCHAR2
|
String
|
getString(NumColonne)
|
NUMERIC (NUMBER sous ORACLE) sans
partie décimale
|
int ou long ou
java.math.BigDecimal
|
getInt(Numcolonne) ou
getLong(NumColonne) ou getBigDecimal(NumColonne)
|
NUMERIC (NUMBER sous ORACLE) avec
partie décimale
|
double ou float
|
getDouble(Numcolonne) ou
getFloat(NumColonne) ou
getBigDecimal(NumColonne)
|
SMALLINT
|
short
|
getShort(NumColonne)
|
INTEGER
|
int
|
getInt(NumColonne)
|
DATE
|
Java.sql.Date
|
getDate(NumColonne)
|
TIME ou partie heure de
DATE
|
java.sql.Time
|
getTime(NumColonne)
|
Remarques :
.
Ces méthodes sont surchargées pour permettre la transmission du nom
de la colonne à la place de son numéro.
|
System.out.println("Numéro employé
: " + resultat.getObject(1));
System.out.println("Nom : "+" " +
resultat.getObject(2));
System.out.println("Date d'embauche
: "+" " + resultat.getDate("hiredate" ));
|
3.1.3
Exemple global
Version avec renvoi des
exceptions :
import java.sql.*;
class ExGlobal
{
public
static void main (String args[]) throws
ClassNotFoundException,
SQLException
{
String cochaine="jdbc:odbc:ora8";
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
Connection conn =
DriverManager.getConnection(cochaine,"scott","tiger");
Statement requete =
conn.createStatement();
ResultSet resultat = requete.executeQuery("SELECT
empno,ename,sal,hiredate FROM EMP");
if
(resultat.next())
{
System.out.println("Numéro Nom
Salaire Date d'embauche");
do
{
// Si on n’avait pas utilisé le pont JDBC/ODBC pour la
// connexion, on n’aurait pas pu utiliser
getObject.
System.out.println(resultat.getObject(1)+"\t"+
resultat.getObject(2) + "\t" + resultat.getFloat("sal")+ "\t"
+
resultat.getDate("hiredate"));
}
while (resultat.next()) ;
}
else
System.out.println (“La table des employés est vide”);
resultat.close() ; // Libération des ressources
occupées par l’objet resultat
requete.close() ; // Libération des ressources occupées
par l’objet requete
conn.close(); // Libération des ressources occupées par
l’objet conn
}
}
Version avec interception des
exceptions :
import java.sql.*;
class ExGlobal
{
public
static void main (String args[])
{
try
{
String cochaine="jdbc:odbc:ora8";
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
Connection conn =
DriverManager.getConnection(cochaine,"scott","tiger");
Statement requete =
conn.createStatement();
ResultSet resultat = requete.executeQuery
("SELECT empno,ename,sal,hiredate FROM EMP");
if
(resultat.next())
{
System.out.println ("Numéro Nom
Salaire Date d'emb.");
do
{
// Si on n’avait pas utilisé le pont JDBC/ODBC pour la
// connexion, on n’aurait pas pu utiliser
getObject.
System.out.println(resultat.getObject(1)+"\t"+
resultat.getObject(2) + "\t" + resultat.getFloat("sal")+
"\t"
+ resultat.getDate(4));
}
while (resultat.next()) ;
}
else
System.out.println (“La table des employés est vide”);
resultat.close() ;
requete.close() ;
conn.close();
}
catch(SQLException e)
{
System.out.println("Erreur\n" + e.getMessage()); // e
contient le nom
// de l’exception et le message d’erreur, e.getMessage()
renvoie
// uniquement le message d’erreur
e.printStackTrace(); // Affichage de la pile des appels de
méthodes
}
catch(ClassNotFoundException e)
{
System.out.println("Le pilote JDBC n'a pas été trouvé");
e.printStackTrace(); // Affichage de la pile des appels de
méthodes
}
}
}
3.1.4
Méta-données
Il est possible de récupérer des informations sur la base (nom
du SGBD, nom des tables, des index…, nom de l’utilisateur courant…)
grâce à l’interface DataBaseMetaData et des informations sur
un ResultSet (nombre de colonnes, nom, type… des colonnes) grâce à
l’interface ResultSetMetaData.
DatabaseMetaData
dbmd =
conn.getMetaData(); // retourne les méta-données
de la base
System.out.println(dbmd.getDatabaseProductName());
System.out.println(dbmd.getUserName());
Cette interface fournit des
informations sur un ResultSet, comme :
- nombre de colonnes :
getColumnCount()
- nom d’une colonne :
getColumnName(NuméroColonne)
- label d’une colonne :
getColumnLabel(NuméroColonne)
- type d’une colonne :
getColumnType(NuméroColonne)
Pour le type, le problème est que
pour NUMERIC (ou NUMBER sous Oracle), la méthode renvoie 3 qu’il y
ait une partie décimale ou non.
Exemple : on reprend l’exemple précédent en utilisant l’interface ResultSetMetaData pour l’affichage de l’en-tête.
if (resultat.next())
{
ResultSetMetaData rsmd;
rsmd = resultat.getMetaData();
for (int
i=1;i<=rsmd.getColumnCount();i++)
System.out.print(rsmd.getColumnName(i) + "\t");
System.out.println("");
do
System.out.println(resultat.getObject(1)+"\t"+
resultat.getObject(2) +
"\t" + resultat.getFloat("sal")+ "\t" +
resultat.getDate("hiredate"));
while (resultat.next()) ;
}
else
System.out.println (“La table des
employés est vide”);
3.1.5
Requête paramétrée
Il
est tout à fait possible de concaténer des parties fixes et des
parties variables pour constituer le texte de la
requête.
Exemple avec une date :
String demb =
"02-04-1981";
ResultSet resultat = requete.executeQuery
("SELECT
empno,ename, sal FROM EMP where hiredate >= '" + demb +
"'");
Exemple avec un entier
:
int dept =
10;
ResultSet resultat = requete.executeQuery
("SELECT empno,ename,sal,hiredate FROM EMP where deptno = " +
dept);
3.1.6
Requête précompilée
PreparedStatement pstmt =
conn.prepareStatement
("select empno,ename from emp where ename = ?");
- Alimentation des paramètres
(si requête paramétrée) :
setType
(NuméroParamètre, Valeur) sur l’objet
PreparedStatement.
pstmt.setString(1,"MILLER");
// renseignement du 1er et seul paramètre avec
la
// valeur MILLER
Les principales méthodes
setType sont :
setString(NumColonne,
Valeur)
setInt(NumColonne,
Valeur)
setLong(NumColonne,
Valeur)
setFloat(NumColonne,
Valeur)
setDouble(NumColonne,
Valeur)
setDate(NumColonne,
Valeur)
- Exécution de la
requête :
ResultSet rs = pstmt.executeQuery();
Remarque : si on souhaite exécuter
plusieurs fois la requête, il suffira d’appliquer uniquement la
méthode executeQuery à l’objet PreparedStatement, il n’y pas à
nouveau compilation de la requête. Ceci est vrai même pour une
requête paramétrée car en fait la requête est précompilée sans les
valeurs des paramètres. Bien sûr, si on souhaite modifier la valeur
des paramètres, il faudra à nouveau utiliser les méthodes
setType.
import java.sql.*;
import java.io.*;
class Reqpar
{
public static void main (String args[]) throws
ClassNotFoundException,
SQLException, IOException
{
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection
("jdbc:oracle:thin:@192.168.10.36:1521:btsig", "scott",
"tiger");
PreparedStatement pstmt = conn.prepareStatement
("select empno,ename,sal,job from emp where deptno =
?");
do
{
pstmt.setInt (1,Entrée.entier("\nSaisir le n° de
département"));
// Si le numéro saisi n’existe pas, l’en-tête seule sera
affichée
System.out.println ("Numéro Nom
Salaire Date d'embauche");
ResultSet rs = pstmt.executeQuery();
while (rs.next())
System.out.println (rs.getInt(1)+" " + rs.getString(2)+
" " + rs.getFloat(3) + " " + rs.getString(4));
rs.close();
}
while (Entrée.car("\nAutre département(O/N)")=='O');
pstmt.close() ;
conn.close();
}
}
3.1.7
Traitement avancé des ResultSet
Il
est possible de déclarer un ResultSet sur lequel on peut effectuer
les déplacements et positionnements que l’on veut, on parle alors
de ResultSet navigable.
Jusqu’à maintenant, nous avons créé
un Statement sans préciser de paramètres, le ResultSet est alors
uniquement en consultation et peut être uniquement parcouru du
début à la fin (pas de retour ni de positionnement possible), il
est dit non navigable.
Il
est possible de préciser 2 paramètres :
1er
paramètre :navigabilité et visibilité des
mises à jour des autres :
ResultSet.TYPE_FORWARD_ONLY :
non navigable et on ne voit pas les mises à jour des
autres
ResultSet.TYPE_SCROLL_INSENSITIVE :
navigable et on ne voit pas les mises à jour des
autres
ResultSet.TYPE_SCROLL_SENSITIVE : navigable et on voit les mises à
jour des autres.
2ème
paramètre : lecture
seule ou mise à jour :
ResultSet.CONCUR_READ_ONLY
ResultSet.CONCUR_UPDATABLE
Remarque : les 2 paramètres
qui viennent d’être étudiés pour le createStatement peuvent être
mis en 2ème et 3ème paramètres du
preparedStatement.
Méthodes pour les déplacements en plus de la méthode next():
public void afterLast() // Positionne le pointeur après le dernier ergt
public void beforeFirst() // Positionne le pointeur avant le premier ergt
public boolean previous() // Positionne le pointeur sur l’ergt précédent et retourne true si OK
// et false sinon
public boolean first() // Déplace le pointeur sur le 1er ergt et retourne true si OK et false
// sinon
public boolean last() // Déplace le pointeur sur le dernier ergt et retourne true si OK et
// false sinon
public boolean relative(int nombre_enregistrements) // Déplace le pointeur de
nombre_enregistrements plus loin ou recule de nombre_enregistrements (si
valeur négative). Il faut déjà être positionné sur un ergt
public boolean absolute(int position) // Déplace le pointeur sur l’enregistrement
// indiqué (la position est numérotée à partir de 1). Ne nécessite
// pas de positionnement préalable.
Exemple
:
import java.sql.*;
class navigation
{
public static void main (String args[]) throws
ClassNotFoundException,
SQLException
{
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection
("jdbc:oracle:thin:@192.168.10.36:1521:btsig", "scott",
"tiger");
Statement requete = conn.createStatement
(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet resultat = requete.executeQuery
("SELECT empno,ename, sal,hiredate FROM EMP");
resultat.afterLast();
if (resultat.previous())
{
ResultSetMetaData rsmd;
rsmd = resultat.getMetaData();
for (int i=1;i<=rsmd.getColumnCount();i++)
System.out.print(rsmd.getColumnLabel(i) + "\t");
System.out.println("");
do
System.out.println(resultat.getInt(1)+"\t"+
resultat.getString(2) + "\t" + resultat.getFloat("sal")+
"\t"
+ resultat.getDate(4));
while (resultat.previous()) ;
// Après avoir traité
le ResultSet d'arrière en avant, on peut très bien
// aller au début et le traiter du début à la fin
resultat.beforeFirst();
resultat.next();
// …
resultat.close();
requete.close();
conn.close();
}
}
}
3.2
Mise à jour (au sens large) de la base de données
La mise à jour d'une
base de données peut être effectuée :
- par le biais d'une requête SQL de
type UPDATE, INSERT ou DELETE exécutée avec la méthode
executeUpdate("Requête") de la classe Statement. Le résultat
renvoyé par l'exécution de la requête indiquera le nombre de lignes
mises à jour dans la base, contrairement à une requête de sélection
qui renvoie un ResultSet.
ou
- avec un ResultSet modifiable : il
faut déclarer le Statement avec CONCUR_UPDATABLE. On parle alors de
ResultSet modifiable. Attention, il ne faut pas utiliser le pont
JDBC-ODBC pour traiter un ResultSet modifiable s’il s’agit d’une
base Oracle.
3.2.1
Ajout
·
En utilisant une
requête SQL
Exemple :
Statement st =
conn.createStatement();
st.executeUpdate("INSERT INTO dept values (50,
‘INFORMATIQUE’, ‘BATIMENT C’") ;
//
Si on effectue l’INSERT avec SELECT au lieu de VALUES alors il est
possible d’insérer
//
plusieurs lignes à la fois et il peut être intéressant de récupérer
le nombre de lignes insérées
int nbIns =
st.executeUpdate("INSERT INTO dept SELECT …") ;
System.out.println (nbIns + "
ligne(s) insérée(s)");
|
·
En
utilisant un curseur
Exemple :
ResultSet resultat
= requete.executeQuery
("SELECT empno,ename, sal,hiredate, deptno FROM EMP");
resultat.moveToInsertRow();
// On se met en insertion
resultat.updateInt(1,7000);
resultat.updateString(2,"TITI");
resultat.updateFloat("sal",100);
resultat.updateInt(5,30);
resultat.insertRow();
// On ajoute la ligne dans la table
|
Les principales méthodes
updateType sont :
updateString(NumColonne,
Valeur)
updateInt(NumColonne,
Valeur)
updateLong(NumColonne,
Valeur)
updateFloat(NumColonne,
Valeur)
updateDouble(NumColonne,
Valeur)
updateDate(NumColonne,
Valeur)
Remarque :
Ainsi qu'on peut le voir dans
l'exemple, ces méthodes sont surchargées pour permettre la
transmission du nom de la colonne à la place de son
numéro.
3.2.2
Modification
·
En
utilisant une requête SQL
Exemple :
st.executeUpdate("UPDATE emp set sal = sal *
1.05”); // st est un Statement
·
En
utilisant un curseur
On est positionné
sur un enregistrement, on effectue la modification des champs puis
on effectue la modification de l’enregistrement.
Exemple :
ResultSet resultat
= requete.executeQuery
("SELECT empno,ename, sal,hiredate FROM EMP");
if
(resultat.next())
{
do
{
"\t" +
resultat.getFloat("sal")+ "\t" + resultat.getDate(4));
float nouvsal = Entrée.flottant
resultat.updateFloat(3,
nouvsal);
resultat.updateRow();
while (resultat.next()) ;
}
|
|
3.2.3
Suppression
·
Par
une requête SQL
st.executeUpdate("DELETE FROM emp where ename =
'MILLER'");
·
En
utilisant un curseur
L'exemple qui suit
montre la suppression de la ligne 5 d'un ResultSet.
ResultSet resultat
= requete.executeQuery
("SELECT empno,ename, sal,hiredate FROM EMP");
resultat.absolute(5);
resultat.deleteRow(); // Suppression de la ligne en
cours
|
3.2.4
Requête de mise à jour (au sens
large) paramétrée
C’est le même principe que pour un
SELECT c’est-à-dire que la requête INSERT, UPDATE ou DELETE peut
contenir des parties fixes et des parties variables.
3.2.5
Requête de mise à jour (au sens
large) précompilée
Cela s’effectue avec un objet
PreparedStatement comme pour une requête de consultation (excepté
que l’exécution sera effectuée avec executeUpdate). La requête
pourra être paramétrée ou non.
Exemple :
import java.sql.*;
import java.util.*;
class maj2
{
public static void main (String args[]) throws
ClassNotFoundException,
SQLException
{
String url="jdbc:odbc:micor";
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
Connection conn =
DriverManager.getConnection(url,"scott","tiger");
java.sql.Date dsys = new
java.sql.Date(System.currentTimeMillis());
PreparedStatement pstmt = conn.prepareStatement
("insert into emp values
(3,'TOTO','nn',7000,?,10,10,10)");
pstmt.setDate(1,dsys);
pstmt.executeUpdate();
}
}
DEVELOPPEMENT CLIENT-SERVEUR EN JAVA
(suite)
Bref rappel de la
1ère partie à partir d’un exemple :
import java.sql.*;
class ExGlobal
{
public
static void main (String args[]) throws
ClassNotFoundException,
SQLException
{
String cochaine="jdbc:odbc:ora8";
Class.forName ("sun.jdbc.odbc.JdbcOdbcDriver");
Connection conn =
DriverManager.getConnection(cochaine,"scott","tiger");
Statement st =
conn.createStatement();
ResultSet resultat = st.executeQuery("SELECT empno, ename,
sal,
hiredate FROM EMP");
if (resultat.next())
{
System.out.println("Numéro Nom
Salaire Date d'embauche");
do
{
// Si on n’avait pas utilisé le pont JDBC/ODBC pour la
// connexion, on n’aurait pas pu utiliser
getObject.
System.out.println(resultat.getObject(1)+"\t"+
resultat.getObject(2) + "\t" + resultat.getFloat("sal")+ "\t"
+
resultat.getDate("hiredate"));
}
while (resultat.next()) ;
}
else
System.out.println (“La table des employés est vide”);
resultat.close() ; // Libération des ressources
occupées par l’objet resultat
st.close() ; // Libération des ressources occupées par
l’objet st
conn.close(); // Libération des ressources occupées par
l’objet conn
}
}
4
Les requêtes de définition ou
de contrôle des données
Avec la méthode executeUpdate, on
peut également effectuer toute requête du LDD
(Exemple : create table) ou du LCD
(Exemple : grant).
Exemple :
Statement st = conn.createStatement();
st.executeUpdate ("grant all on emp to public");
4.1
La validation/annulation des
mises à jour (au sens large)effectuées
Avec JDBC, par défaut, un commit
est effectué après chaque mise à jour. Si on ne souhaite pas le
mode par défaut, il faut passer en mode manuel :
conn.setAutocommit(false);
et
ensuite, il faudra se charger du commit ou
rollback :
conn.commit();
ou
conn.rollback();
On
peut repasser à tout moment en mode autocommit :
conn.setAutocommit(true) ;
Il
faut préciser que par défaut (mode autocommit) : en cas de
plantage, les mises à jour effectuées ne sont pas
« défaites » par jdbc ; ceci est dû au fait qu’un
commit est effectué après chaque instruction.
5
Le client-serveur de traitements
5.1
L'exécution de procédures stockées
On utilise pour cela l'interface CallableStatement qui dérive de PreparedStatement.
Bien sûr, une procédure stockée peut être paramétrée.
Il faut d'abord préparer l'appel puis valoriser les paramètres (si procédure paramétrée) puis exécuter l'appel.
5.1.1
Syntaxe
· Préparation :
Sans paramètres :
CallableStatement cStmt =
conn.prepareCall("{call nom_Procédure}");
Avec paramètres :
CallableStatement cStmt = conn.prepareCall("{call nom_Procedure (?, ?, ...)}");
Il faut ensuite valoriser les paramètres au moyen des méthodes setType (setInt …). Si les paramètres sont uniquement en entrée, on peut indiquer leurs valeurs directement dans l'appel à la méthode prepareCall.
· Exécution :
cStmt.executeUpdate();
5.1.2
Exemples
CallableStatement cStmt =conn.prepareCall("{call
majsal}");
cStmt.executeUpdate();
Code PL/SQL de la procédure
:
CREATE PROCEDURE majsal
BEGIN
DECLARE NumSal int;
DECLARE montant float;
SET NumSal = 7934;
SET montant = 100;
Update emp set sal = sal + montant
Where empno = NumSal ;
END
majsal ;
- Procédure avec paramètres en
entrée
CallableStatement cStmt =conn.prepareCall("{call
majsal2(?,?)}");
cStmt.setInt(1,7934);
cStmt.setFloat(2,5000.5F);
cStmt.executeUpdate();
On
pouvait également transmettre directement les valeurs des
paramètres à la méthode prepareCall :
CallableStatement cStmt =conn.prepareCall("{call
majsal2(7934,5000.5F)}");
cStmt.executeUpdate();
Code PL/SQL de la procédure
:
create procedure majsal2(x_empno int, x_sal
float)
BEGIN
UPDATE EMP SET sal = sal + x_sal WHERE
empno = x_empno;
END majsal2;
- Procédure avec paramètres en entrée
et en entrée-sortie
CallableStatement cStmt =
conn.prepareCall("{call majsal3(?,?)}");
cStmt.setInt(1,7934);
cStmt.setFloat(2,4000);
cStmt.registerOutParameter(2,
Types.FLOAT); // Obligatoire
pour les paramètres en
// e/s ou en sortie
cStmt.executeUpdate();
System.out.println("Nouveau
salaire : " + cStmt.getFloat(2));
Code PL/SQL de la procédure
:
create or replace procedure majsal3(x_empno in int, x_sal
in out float)
BEGIN
UPDATE EMP SET sal = sal + x_sal WHERE
empno = x_empno;
select sal into x_sal from emp where
empno = x_empno;
END majsal3;
Remarques : le principe pour
les paramètres en sortie est le même que pour les paramètres en e/s
excepté qu’il n’y a pas d’instruction setType pour alimenter
le paramètre.
Des problèmes se posent pour les
paramètres en e/s et en sortie si on utilise le pont
jdbc/odbc.
5.2
L'exécution de fonctions stockées
C'est le même principe que pour les
procédures stockées excepté qu'il y a un paramètre de
retour.
5.2.1
Syntaxe
· Préparation :
Sans paramètres :
CallableStatement cStmt =
conn.prepareCall("{? = call nom_Fonction}");
Avec paramètres :
CallableStatement cStmt = conn.prepareCall("{? = call nom_Fonction (?, ?, ...)}");
Il faut ensuite valoriser les paramètres au moyen des méthodes setType (setInt…). Si les paramètres sont uniquement en entrée, on peut indiquer leurs valeurs directement dans l'appel à la méthode prepareCall.
· Exécution :
cStmt.executeUpdate();
Ensuite, il faut récupérer le paramètre de retour au moyen d'une méthode getType.
5.2.2
Exemple
CallableStatement cStmt = conn.prepareCall("{? = call
MoyenneAnc('SALESMAN')}");
cStmt.registerOutParameter(1,Types.INTEGER);
// Obligatoire pour le paramètre de retour
cStmt.executeUpdate();
System.out.println(cStmt.getInt(1));
// Le paramètre de retour est toujours le 1er paramètre
Code PL/SQL de la fonction
:
CREATE
FUNCTION MoyenneAnc (jobsai varchar(40))
RETURNS
int
BEGIN
DECLARE ancmoyen int;
select
avg(YEAR(sysdate') - YEAR(hiredate))
into ancmoyen
from emp
where job = jobsai;
RETURN
ancmoyen;
END MoyenneAnc;